package top.hcode.hoj.common;

import cn.hutool.core.util.StrUtil;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.sagacity.sqltoy.dao.impl.SqlToyLazyDaoImpl;
import org.sagacity.sqltoy.model.Page;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author orangej
 * @since 2024/9/17
 */
@Component
public class LazyDao extends SqlToyLazyDaoImpl {

    public Long runSql(String sqlOrSqlId, Object... objects) {
        if (objects == null || objects.length == 0) {
            return super.executeSql(sqlOrSqlId, null, null);
        }
        if (objects.length == 1 && objects[0] instanceof Map) {
            return super.executeSql(sqlOrSqlId, (Map) objects[0]);
        }

        int index = 0;
        String[] names = new String[objects.length];
        while (sqlOrSqlId.contains("?")) {
            names[index] = "p" + index;
            sqlOrSqlId = sqlOrSqlId.replaceFirst("\\?", String.format(":%s", names[index]));

            index++;
        }

        return super.executeSql(sqlOrSqlId, names, objects);
    }

    Long runSql(String sqlOrSqlId, Map<String, Object> params) {
        return super.executeSql(sqlOrSqlId, params);
    }

    public String fetchString(String sqlOrSqlId, Object... objects) {
        return selectOne(String.class, sqlOrSqlId, objects);
    }

    public Long fetchLong(String sqlOrSqlId, Object... objects) {
        return selectOne(Long.class, sqlOrSqlId, objects);
    }

    public Integer fetchInt(String sqlOrSqlId, Object... objects) {
        return selectOne(Integer.class, sqlOrSqlId, objects);
    }

    public NutMap fetchMap(String sqlOrSqlId, Object... objects) {
        return selectOne(NutMap.class, sqlOrSqlId, objects);
    }

    public List<String> queryString(String sqlOrSqlId, Object... objects) {
        return selectList(String.class, sqlOrSqlId, objects);
    }

    public List<Long> queryLong(String sqlOrSqlId, Object... objects) {
        return selectList(Long.class, sqlOrSqlId, objects);
    }

    public List<Integer> queryInt(String sqlOrSqlId, Object... objects) {
        return selectList(Integer.class, sqlOrSqlId, objects);
    }

    public List<NutMap> queryMap(String sqlOrSqlId, Object... objects) {
        return selectList(NutMap.class, sqlOrSqlId, objects);
    }

    public <T> T selectOne(Class<T> clazz, String sqlOrSqlId, Object... objects) {
        Map params = new HashMap();
        String sql = sqlOrSqlId;
        if (isParamMap(objects)) {
            params = (Map) objects[0];
        } else {
            sql = convertSqlParamNames(sqlOrSqlId, objects, params);
        }

        return (T) super.loadBySql(sql, params, clazz);
    }

    public <T> List<T> selectList(Class<T> clazz, String sqlOrSqlId, Object... objects) {
        Map params = new HashMap();
        String sql = sqlOrSqlId;
        if (isParamMap(objects)) {
            params = (Map) objects[0];
        } else {
            sql = convertSqlParamNames(sqlOrSqlId, objects, params);
        }

        return super.findBySql(sql, params, clazz);
    }

    public <T> Page<T> selectPage(int pageNo, int pageSize, Class<T> resultType, String sqlOrSqlId, Object... objects) {
        Map params = new HashMap();
        String sql = sqlOrSqlId;
        if (isParamMap(objects)) {
            params = (Map) objects[0];
        } else {
            sql = convertSqlParamNames(sqlOrSqlId, objects, params);
        }
        Page<T> page = new Page<>(pageSize, pageNo);
        return super.findPageBySql(page, sql, params, resultType);
    }

    private String convertSqlParamNames(String sqlOrSqlId, Object[] objects, Map params) {
        if (!sqlOrSqlId.contains("?")) {
            return sqlOrSqlId;
        }

        int index = 0;
        while (sqlOrSqlId.contains("?")) {
            String name = "p" + index;
            sqlOrSqlId = sqlOrSqlId.replaceFirst("\\?", String.format(":%s", name));
            params.put(name, objects[index]);

            index++;
        }
        return sqlOrSqlId;
    }

    private boolean isParamMap(Object[] objects) {
        if (objects != null && objects.length == 1) {
            return objects[0] instanceof Map || objects[0] instanceof NutMap;
        }

        return false;
    }

    public List<NutMap> findMapBySql(String sqlOrSqlId, NutMap params) {
        return super.findBySql(sqlOrSqlId, params, NutMap.class);
    }

    public List<String> findStringBySql(String sqlOrSqlId, NutMap params) {
        return super.findBySql(sqlOrSqlId, params, String.class);
    }

    public List<Long> findLongBySql(String sqlOrSqlId, NutMap params) {
        return super.findBySql(sqlOrSqlId, params, Long.class);
    }

    public List<Integer> findIntBySql(String sqlOrSqlId, NutMap params) {
        return super.findBySql(sqlOrSqlId, params, Integer.class);
    }


    /**
     * 查询结果集，返回Map，key的值通过keyMapper获取，value类型为resultType，是sql的返回结果
     */
    public <K, V> Map<K, V> findBySqlToMap(String sqlOrSqlId, NutMap params,
                                           Class<V> resultType, Function<V, K> keyMapper) {
        return findBySqlToMap(sqlOrSqlId, params, resultType, keyMapper, Function.identity());
    }

    /**
     * 查询结果集，返回Map，key的值通过keyMapper获取，value的值通过valueMapper获取
     */
    public <K, V, R> Map<K, V> findBySqlToMap(String sqlOrSqlId, NutMap params,
                                              Class<R> resultType,
                                              Function<R, K> keyMapper,
                                              Function<R, V> valueMapper) {
        List<R> rows = super.findBySql(sqlOrSqlId, params, resultType);
        if (Lang.isEmpty(rows)) {
            return new HashMap<>();
        }

        return rows.stream().collect(Collectors.toMap(keyMapper, valueMapper));
    }

    /**
     * 插入记录
     *
     * @param table   表名
     * @param columns 字段名和值
     * @return 插入数量
     */
    public Long insert(String table, NutMap columns) {
        Set<String> keySet = columns.keySet();

        String valuePart = "";
        for (String key : keySet) {
            valuePart += ":" + key + ",";
        }
        valuePart = StrUtil.removeSuffix(valuePart, ",");

        String sql = String.format("INSERT INTO %s (%s) VALUES (%s)", table, StrUtil.join(",", keySet), valuePart);
        return super.executeSql(sql, toParamMap(columns));
    }

    /**
     * 更新表记录，不会更新查询条件中的字段
     *
     * @param table   表名
     * @param columns 字段名和值
     * @param where   查询条件
     * @return 更新数量
     */
    public Long update(String table, NutMap columns, String where) {
        Set<String> keySet = columns.keySet();

        String valuePart = "";
        for (String key : keySet) {
            if (where.contains(":" + key)) {
                // 忽略查询条件中的字段
                continue;
            }
            valuePart += key + "=:" + key + ",";
        }
        valuePart = StrUtil.removeSuffix(valuePart, ",");

        String sql = String.format("UPDATE %s SET %s WHERE %s", table, valuePart, where);
        return super.executeSql(sql, toParamMap(columns));
    }

    Map<String, Object> toParamMap(NutMap params) {
        Map<String, Object> map = new HashMap<>();
        for (String key : params.keySet()) {
            map.put(key, params.get(key));
        }
        return map;
    }
}
